package com.ruoyi.business.interceptor;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.business.config.ResponseCode;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;

/**
 * interceptor for api sign
 *
 * @author JerryLu
 * @date 2024/8/22 8:32
 * @return
 */
@Component
@Configuration
public class SignInterceptor implements HandlerInterceptor {
    private static final String APP_SECRET = "veB+aE2JX3BVv7tz0um/7Ke5EYAcRIw";
    @Resource
    private RedisCache redisCache;

    /**
     * 签名相关校验
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  处理对象：controller中的信息
     * @return true 正常, false 被拦截
     * @throws Exception
     * @author JerryLu
     * @date 2024/8/22 8:33
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //依次检查各变量是否存在
        String messageId = request.getHeader("messageId");
        if (StringUtils.isBlank(messageId)) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.NO_MESSAGEID, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        String timestampStr = request.getHeader("timestamp");
        if (StringUtils.isBlank(timestampStr)) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.NO_TIMESTAMP, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        String signStr = request.getHeader("signature");
        if (StringUtils.isBlank(signStr)) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.NO_SIGN, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        long timestamp = Long.parseLong(timestampStr);
        //前端传过来的时间戳与服务器当前时间戳差值大于3分钟，则当前请求的timestamp无效
        if (Duration.between(Instant.ofEpochMilli(timestamp), Instant.ofEpochMilli(System.currentTimeMillis())).toMinutes() > 3) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.SIGN_TIMESTAMP_INVALID, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        //判断redis中的messageId，确认当前请求是否为重复请求，控制API接口幂等性
        boolean nonceExists = redisCache.hasKey(timestampStr + messageId);
        if (nonceExists) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.SIGN_DUPLICATION, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        // 通过后台MD5重新签名校验与前端签名sign值比对，确认当前请求数据是否被篡改
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        String body = stringBuilder.toString();
        if (StringUtils.isBlank(body)) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.DATA_DECRYPT_FAIL, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        JSONObject jsonObj = JSON.parseObject(body);
        String origin = APP_SECRET + "_" + timestampStr + "_" + jsonObj.get("data");
        String signEcrypt = Md5Utils.hash(origin);
        if (!(signStr.equalsIgnoreCase(signEcrypt))) {
            String msg = JSON.toJSONString(ResultUtil.error(ResponseCode.SIGN_VERIFY_FAIL, null));
            ServletUtils.renderString(response, msg);
            return false;
        }
        //将messageId存进redis
        redisCache.setCacheObject(timestampStr + messageId, messageId, 180, TimeUnit.SECONDS);

        //sign校验无问题,放行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
